/**
 * Copyright (C) 2009 aileron.cc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cc.aileron.orm.factory;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import cc.aileron.accessor.PojoAccessorFactory;
import cc.aileron.commons.instance.InstanceFactory;
import cc.aileron.commons.resource.ResourceNotFoundException;
import cc.aileron.orm.G2Dao;
import cc.aileron.orm.G2DaoFindCondition.Paging;
import cc.aileron.orm.factory.serial.G2DaoSerial;
import cc.aileron.orm.logger.SqlLogger;
import cc.aileron.orm.sql.G2DaoSqlMap;
import cc.aileron.template.parser.ParserMethodNotFoundException;
import cc.aileron.template.reader.TemplateSyntaxEexception;

import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;

/**
 * 実装
 */
@Singleton
public class G2DaoFactoryImpl implements G2DaoFactory
{
    public <T> G2Dao<T> create(final Class<T> targetClass) throws IOException,
            ResourceNotFoundException,
            TemplateSyntaxEexception,
            ParserMethodNotFoundException
    {
        sqlMap.compile(targetClass);
        return new G2Dao<T>()
        {
            public int count(final Object findCondition)
            {
                if (findCondition == null)
                {
                    throw new IllegalArgumentException();
                }
                final Connection connection = getConnection();
                try
                {
                    return executor.count(connection, findCondition);
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
                finally
                {
                    closeConnection(connection);
                }
            }

            public void insert(final T bean)
            {
                if (bean == null)
                {
                    throw new IllegalArgumentException();
                }
                final Connection connection = getConnection();
                try
                {
                    executor.insert(connection, bean);
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
                finally
                {
                    closeConnection(connection);
                }
            }

            public void delete(final T bean)
            {
                if (bean == null)
                {
                    throw new IllegalArgumentException();
                }
                final Connection connection = getConnection();
                try
                {
                    executor.delete(connection, bean);
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
                finally
                {
                    closeConnection(connection);
                }
            }

            public boolean findExist(final Object findCondition)
            {
                if (findCondition == null)
                {
                    throw new IllegalArgumentException();
                }
                final Connection connection = getConnection();
                try
                {
                    return executor.exist(connection, findCondition);
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
                finally
                {
                    closeConnection(connection);
                }
            }

            public Iterable<T> findIterable(final Object object)
            {
                throw new UnsupportedOperationException("まだ実装を考えて無い");
                // final Template sqlTemplate = map.get(targetClass,
                // "findBy" + object.getClass()
                // .getSimpleName());
            }

            public List<T> findList(final Object findCondition)
            {
                if (findCondition == null)
                {
                    throw new IllegalArgumentException();
                }
                final Connection connection = getConnection();
                try
                {
                    return executor.findList(connection, findCondition);
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
                finally
                {
                    closeConnection(connection);
                }
            }

            @Override
            public List<T> findList(final Object findCondition,
                    final Paging paging)
            {
                if (findCondition == null)
                {
                    throw new IllegalArgumentException();
                }
                final Connection connection = getConnection();
                try
                {
                    return executor.findList(connection, findCondition, paging);
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
                finally
                {
                    closeConnection(connection);
                }
            }

            public T findOne(final Object findCondition)
            {
                if (findCondition == null)
                {
                    throw new IllegalArgumentException();
                }
                final Connection connection = getConnection();
                try
                {
                    return executor.findOne(connection, findCondition);
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
                finally
                {
                    closeConnection(connection);
                }
            }

            public void initialize()
            {
                final Connection connection = getConnection();
                try
                {
                    executor.initialize(connection);
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
                finally
                {
                    closeConnection(connection);
                }
            }

            public void update(final T bean)
            {
                if (bean == null)
                {
                    throw new IllegalArgumentException();
                }
                final Connection connection = getConnection();
                try
                {
                    executor.update(connection, bean);
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
                finally
                {
                    closeConnection(connection);
                }
            }

            /**
             * @param connection
             */
            private void closeConnection(final Connection connection)
            {
                try
                {
                    connection.close();
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
            }

            /**
             * @return connection
             */
            private Connection getConnection()
            {
                try
                {
                    return dataSource.getConnection();
                }
                catch (final SQLException e)
                {
                    throw new RuntimeException(e);
                }
            }

            /**
             * init
             * 
             * @param injector
             */
            @SuppressWarnings("unused")
            @Inject
            private void init(final Injector injector)
            {
                final InstanceFactory instanceFactory = injector.getInstance(InstanceFactory.class);
                final PojoAccessorFactory accessorFactory = injector.getInstance(PojoAccessorFactory.class);
                final SqlLogger sqlLogger = injector.getInstance(SqlLogger.class);

                dataSource = injector.getInstance(DataSource.class);
                executor = new G2DaoExecutorImpl<T>(targetClass, serial,
                        instanceFactory, accessorFactory, sqlMap, sqlLogger);
            }

            private DataSource dataSource;

            private G2DaoExecutor<T> executor;
        };
    }

    /*
     * (非 Javadoc)
     * 
     * @see cc.aileron.orm.factory.G2DaoFactory#create(java.lang.Class)
     */
    /**
     * constractor injection
     * 
     * @param sqlMap
     * @param serial
     */
    @Inject
    public G2DaoFactoryImpl(final G2DaoSqlMap sqlMap, final G2DaoSerial serial)
    {
        this.sqlMap = sqlMap;
        this.serial = serial;
    }

    final G2DaoSerial serial;

    final G2DaoSqlMap sqlMap;
}